/*  
 *  Copyright 2006 Paul Jack.
 *
 *  This file is part of the Dex compiler suite. 
 *  
 *  Dex is free software; you can redistribute it and/or modify it
 *  under the terms of the GNU General Public License as published by the
 *  Free Software Foundation; either version 2 of the License, or (at your
 *  option) any later version.
 *  
 *  Dex is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 *  Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
 */
package dex.compiler.interpreter;


import dex.compiler.checker.type.IntegerType;
import dex.compiler.interpreter.value.BooleanValue;
import dex.compiler.interpreter.value.IntegerValue;
import dex.compiler.interpreter.value.Value;
import dex.compiler.model.expression.Infix;


/**
 * Isolates the routines for evaluating infix expressions.
 */
class InfixEvaluator {
	

	/**
	 * Performs an infix operation on two arguments, returning the result.
	 * 
	 * @param infix  the infix operator to apply
	 * @param left   the left argument for the operator
	 * @param right  the right argument for the operator
	 * @return  the result of the operator
	 */
	public static Value operate(Infix infix, Value left, Value right) {
		if (left instanceof IntegerValue) {
			return operate(infix, (IntegerValue)left, (IntegerValue)right);
		} else {
			// FIXME: Local values compared by value?
			return equality(infix, left, right);
		}
	}


	/**
	 * Performs an infix operation on two integer arguments.
	 * 
	 * @param infix  the operator to apply
	 * @param leftValue  the left argument for the operator
	 * @param rightValue  the right argument for the operator
	 * @return  either an integer or boolean result value
	 */
	private static Value operate(Infix infix, IntegerValue leftValue, IntegerValue rightValue) {
		IntegerType leftType = (IntegerType)leftValue.type();
		IntegerType rightType = (IntegerType)rightValue.type();
		int width = Math.max(leftType.getWidth(), rightType.getWidth());
		boolean signed = leftType.isSigned() || rightType.isSigned();
		long left = leftValue.value();
		long right = rightValue.value();
		switch (infix) {
			case ADD:
				return new IntegerValue(signed, width, left + right);
			case SUBTRACT:
				return new IntegerValue(signed, width, left - right);
			case MULTIPLY:
				return new IntegerValue(signed, width, left * right);
			case DIVIDE:
				return new IntegerValue(signed, width, left / right);
			case MODULO:
				return new IntegerValue(signed, width, left % right);
			case AND:
				return new IntegerValue(signed, width, left & right);
			case OR:
				return new IntegerValue(signed, width, left | right);
			case XOR:
				return new IntegerValue(signed, width, left ^ right);
			case SHIFT_LEFT:
				return new IntegerValue(signed, width, left << right);
			case SHIFT_RIGHT:
				return new IntegerValue(signed, width, left >> right);
			case EQUAL:
				return BooleanValue.get(left == right);
			case NOT_EQUAL:
				return BooleanValue.get(left != right);
			case GREATER:
				return BooleanValue.get(left > right);
			case LESS:
				return BooleanValue.get(left < right);
			case GREATER_OR_EQUAL:
				return BooleanValue.get(left >= right);
			case LESS_OR_EQUAL:
				return BooleanValue.get(left <= right);
			default:
				throw new IllegalArgumentException(infix + " can't be applied to integers.");
		}
	}


	/**
	 * Applies an equality operator to the given arguments.
	 * 
	 * @param infix  the operator to apply
	 * @param leftValue  the left argument for the operator
	 * @param rightValue  the right argument for the operator
	 * @return  the boolean result value
	 */
	static Value equality(Infix infix, Value left, Value right) {
		switch (infix) {
			case EQUAL:
				return BooleanValue.get(left == right); // FIXME: locals
			case NOT_EQUAL:
				return BooleanValue.get(left != right); // FIXME: locals
			default:
				throw new IllegalArgumentException(infix + " can't be applied to compound types.");
		}
	}
}
